#include <windows.h>
#include <process.h>

#include <fstream>
#include <string>
#include <sstream>
#include <iostream>

//temp - remove
#include "Renderer\D3D9Renderer.h"
#include "Game\Game.h"
#include "Input\Input.h"

using std::cout;
using std::ifstream;
using std::string;
using std::istringstream;

// Let's get a command console for printing debug information when running DEBUG
#ifdef _DEBUG
	#include "Utility\Misc\DebugOutput.h"
	#pragma comment(linker, "/SUBSYSTEM:Console")
#else
	#pragma comment(linker, "/SUBSYSTEM:Windows")
#endif


// Variables containing important window/graphics related information
int screenWidth;
int screenHeight;
bool vsync;
bool fullscreen;

LPCSTR applicationName = "Project Wake";
HINSTANCE hinstance;
HWND hwnd;

/*
Handle OS/System messages
*/
LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
	switch(umessage)
	{
		case WM_CREATE: 
		{
			RAWINPUTDEVICE Rid[2];
			Rid[0].usUsagePage = 0x01; 
			Rid[0].usUsage = 0x02; 
			Rid[0].dwFlags = RIDEV_DEVNOTIFY;
			Rid[0].hwndTarget = 0;
			Rid[1].usUsagePage = 0x01; 
			Rid[1].usUsage = 0x06; 
			Rid[1].dwFlags = RIDEV_DEVNOTIFY;
			Rid[1].hwndTarget = 0;
			if (RegisterRawInputDevices(Rid, 2, sizeof(Rid[0])) == FALSE)
			{
				MessageBox(HWND_DESKTOP, "ERROR: Failed to register raw input devices.",
					"Raw Input Error", MB_OK | MB_ICONERROR);
				return -1;
			}

			return(0);
		}
		break;

		case WM_INPUT:
		{
			RAWINPUT ri;
			UINT dwSize = sizeof(RAWINPUT);

			GetRawInputData((HRAWINPUT)lparam, RID_INPUT, &ri, &dwSize, sizeof(RAWINPUTHEADER));
			
			if (ri.header.dwType == RIM_TYPEKEYBOARD)
				Input::RawKeyInput(ri.data.keyboard);
			else if (ri.header.dwType == RIM_TYPEMOUSE) 
				Input::RawMouseInput(ri.data.mouse);		

			return 0;
		}
		break;

		case WM_DESTROY:
		{
			PostQuitMessage(0);
			return 0;
		}

		case WM_CLOSE:
		{
			PostQuitMessage(0);		
			return 0;
		}

		default:
		{
			return DefWindowProc(hwnd, umessage, wparam, lparam);
		}
	}
}

/*
We want to initialize all application specific data here
*/
void InitializeApplication()
{
	//Load from .ini all configuration data
	char line[256];
	ifstream startup;

	startup.open("Res/Data/startup.ini");

	while( startup.getline(line,256) )
	{
		istringstream newLine(line, istringstream::in);

		string command;

		newLine >> command;

		if(command == "fullscreen")
			newLine >> fullscreen;
		else if(command == "vsync")
			newLine >> vsync;
		else if(command == "screen_width_fullscreen")
		{
			if(fullscreen)
				newLine >> screenWidth;
		}
		else if(command == "screen_height_fullscreen")
		{
			if(fullscreen)
				newLine >> screenHeight;
		}
		else if(command == "screen_width_windowed")
		{
			if(!fullscreen)
				newLine >> screenWidth;
		}
		else if(command == "screen_height_windowed")
		{
			if(!fullscreen)
				newLine >> screenHeight;
		}
	}

	//Get rid of sticky keys
	STICKYKEYS StartupStickyKeys = {sizeof(STICKYKEYS), 0};
	SystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(STICKYKEYS), &StartupStickyKeys, 0);

	STICKYKEYS skOff = StartupStickyKeys;
    if( (skOff.dwFlags & SKF_STICKYKEYSON) == 0 )
    {
        skOff.dwFlags &= ~SKF_HOTKEYACTIVE;
        skOff.dwFlags &= ~SKF_CONFIRMHOTKEY;
 
        SystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(STICKYKEYS), &skOff, 0);
    }
}

void InitializeWindow()
{
	WNDCLASSEX wc;
	DEVMODE dmScreenSettings;
	int posX, posY, windowWidth, windowHeight;

	DWORD dwWindowStyleFlags = WS_VISIBLE;

	hinstance = GetModuleHandle(NULL);

	wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc   = WndProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinstance;
	wc.hIcon		 = LoadIcon(NULL, IDI_WINLOGO);
	wc.hIconSm       = wc.hIcon;
	wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = applicationName;
	wc.cbSize        = sizeof(WNDCLASSEX);
	
	RegisterClassEx(&wc);

	if(fullscreen)
	{
		dwWindowStyleFlags |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP;

		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
		dmScreenSettings.dmSize       = sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth  = (unsigned long)screenWidth;
		dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
		dmScreenSettings.dmBitsPerPel = 32;			
		dmScreenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);

		windowWidth = screenWidth;
		windowHeight = screenHeight;

		posX = posY = 0;
	}
	else
	{
		dwWindowStyleFlags |= WS_OVERLAPPEDWINDOW;

		posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth)  / 2;
		posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;

		RECT rWindow;
		rWindow.left	= 0;
		rWindow.top		= 0;
		rWindow.right	= screenWidth;
		rWindow.bottom	= screenHeight;

		AdjustWindowRectEx(&rWindow, dwWindowStyleFlags,FALSE, WS_EX_APPWINDOW);

		windowWidth		= rWindow.right - rWindow.left;
		windowHeight	= rWindow.bottom - rWindow.top;
	}

	hwnd = CreateWindowEx(WS_EX_APPWINDOW, applicationName, applicationName, dwWindowStyleFlags,
						    posX, posY, windowWidth, windowHeight, NULL, NULL, hinstance, NULL);

	ShowWindow(hwnd, SW_SHOW);
	SetForegroundWindow(hwnd);
	SetFocus(hwnd);
}

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{
	MSG		msg;

	if ( hPrevInstance ) {
        return -1;
	}
	
	InitializeApplication();

	InitializeWindow();

	bool result = Game::Initialize(hwnd, fullscreen, vsync, screenWidth, screenHeight);

	if(!result)
	{
		return -1;
	}

	ZeroMemory(&msg, sizeof(MSG));

	while(Game::s_bRunning)
	{
		while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		if(msg.message == WM_QUIT)
		{
			Game::s_bRunning = false;
		}

		Game::Run();
	}

	Game::Exit();

	return 0;
}

int main()
{
#ifdef _DEBUG
	DebugOutput::Initialize(DebugOutput::RENDER_MSG | DebugOutput::NETWORK_MSG | DebugOutput::INPUT_MSG);
#endif

	int msg = WinMain(GetModuleHandle(NULL), NULL, GetCommandLine(), SW_SHOWDEFAULT);
	
	return 0;
}